package org.fatewa.engine.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.fatewa.engine.advice.RayquazaAdvice;
import org.fatewa.engine.advice.mapper.PagingDataMapper;
import org.fatewa.engine.design.advice.AbstractOriginalProvider;
import org.fatewa.engine.design.base.PrimaryKeyProvider;
import org.fatewa.engine.design.genius.GenericHooks;
import org.fatewa.engine.endpoint.RayquazaEndpoint;
import org.fatewa.engine.hooks.ValidateHook;
import org.fatewa.engine.magic.RayquazaSqlInterceptor;
import org.fatewa.engine.property.RayquazaProperties;
import org.fatewa.engine.property.RayquazaResponseProperties;
import org.fatewa.engine.property.ResponseCodeMapper;
import org.fatewa.engine.property.ResponseMessageMapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * @author 4everlynn
 */
@Configuration
@EnableConfigurationProperties({RayquazaProperties.class, RayquazaResponseProperties.class, ResponseCodeMapper.class, ResponseMessageMapper.class})
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RayquazaConfig {

    /**
     * 默认配备一个 PrimaryKeyProvider
     *
     * @return PrimaryKeyProvider
     * @see PrimaryKeyProvider
     */
    @Bean
    @ConditionalOnMissingBean(PrimaryKeyProvider.class)
    PrimaryKeyProvider<Long> defaultPrimaryKeyProvider() {
        // 默认主键名称为 ID
        return new PrimaryKeyProvider<Long>() {

            @Override
            public String key() {
                return "id";
            }

            @Override
            public Class<Long> type() {
                return Long.class;
            }

        };
    }

    /**
     * 解决简单类型 Kernel 无法处理的问题
     *
     * @return WebMvcConfigurer
     */
    @Bean
    public WebMvcConfigurer fixStringMessageConverter(ObjectMapper mapper) {
        return new WebMvcConfigurer() {
            @Override
            public void configureMessageConverters(@NotNull List<HttpMessageConverter<?>> converters) {
                converters.add(0, new MappingJackson2HttpMessageConverter(mapper));
            }
        };
    }

    /**
     * 开放 Prometheus 放行
     *
     * @return AdviceOriginalProvider
     */
    @Bean
    AbstractOriginalProvider prometheusWhitelist() {
        return new AbstractOriginalProvider() {
            @Override
            public void configure() {
                add("/actuator/prometheus");
            }
        };
    }

    /**
     * 配置 Mybatis Plus 分页插件
     *
     * @return Mybatis Plus 分页插件
     */
    @Bean
    MybatisPlusInterceptor interceptor() {
        final MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
        pageInterceptor.setOptimizeJoin(false);
        interceptor.addInnerInterceptor(pageInterceptor);
        return interceptor;
    }

    @Bean
    RayquazaEndpoint entrypoint(ApplicationContext context) {
        return new RayquazaEndpoint(context);
    }

    @Bean
    GenericHooks<Object, Object> globalValidateHooks(ApplicationContext context) {
        return new ValidateHook().context(context);
    }

    @Bean
    public RayquazaSqlInterceptor rayquazaSqlInterceptor (ApplicationContext context) {
        return new RayquazaSqlInterceptor(context);
    }

    @Bean
    RayquazaAdvice rayquazaAdvice(RayquazaResponseProperties properties, JacksonProperties jacksonProperties,
                                  ResponseCodeMapper codeMapper, ResponseMessageMapper messageMapper, ApplicationContext context) {
        return new RayquazaAdvice(codeMapper, messageMapper, properties, jacksonProperties, context);
    }

    /**
     * 默认的分页数据映射器
     *
     * @return 默认分页数据映射器
     */
    @Bean
    PagingDataMapper pagingDataMapper() {
        return new PagingDataMapper();
    }

}
